home *** CD-ROM | disk | FTP | other *** search
- /* Fill functions, (C) as the part of KNOW-HOW.DRAW, HOTKEY DRAW,
- HOTKEY PLOT and HOTKEY OBJECT DRAW packages.
- This file contains floodfill function for BGI
- library. The area may be represented as two-dimentional vessel, which
- we fill with water, in the absence of athmosphere.
- */
-
- #include <stdlib.h>
- #include <conio.h>
- #include <mem.h>
-
- #include "pixel.h"
- #include "graphpp.h"
-
- enum { DN, LEFT, RIGHT, L_DN, R_DN, UP, L_UP, R_UP, NO };
-
- rect level[640]; // there are <= 640 elements in this realization,
- // the VGA horiz. dimention. Using left-to-right filling
- // we may use 480 elements
- char flag; // draw_line (1) or go_local (0)
- int used; // number of elements in level[]
- int local; // is it local min ?
- rect work; // used to avoid copying
- rect bound; // outline of the area to fill, used to avoid copying
- int start_color; // color in the start pixel, used to avoid copying
- int attr, bak; // colors, used to avoid additional copying in put_pixel()
- // calls
- char* fill; // fill pattern
- /////////////////////////////
- inline int get_pixel(loc p) // is this pixel
- { // have color ==
- if(getpixel(p) != start_color || !bound.contains(p)) // start_color,
- return 0; // and is it inside
- return 1; // of bound (work
- } // area
-
- inline int get_pixel(int x, int y) { return get_pixel(loc(x, y)); }
- ////////////////////////////
- inline void putPixel(loc pos)
- {
- if(!global_i[8])
- put_pixel(pos.X, pos.Y, global_i[9]);
- else
- put_pixel_error_prop(pos.X, pos.Y, global_i[9], 48);
- }
- ////////////////////////////
- int inside(loc p) // is there any line under current pixel ?
- {
- for(int i = 0; i < used; i++)
- {
- if(p.Y > level[i].origin.Y - 1)
- continue;
- else if(p.Y < level[i].origin.Y - 1)
- return 0;
- if(p.X <= level[i].corner.X && p.X >= level[i].origin.X)
- return 1;
- }
- return 0;
- }
- ///////////////////////
- int go_local(int left = 1) // go to the local minimum under the current
- { // pixel. "left" is used to block left moving
- int i = 1; // if we use the same start and fill colors
- loc pos = getCP(); // and can not begin from step down
- while(1)
- {
- if(get_pixel(pos.X, pos.Y + 1) && !inside(pos))
- { moveto(pos = loc(pos.X, pos.Y + 1)); i = 0; } // left = 1; }
- else if(left && get_pixel(pos.X - 1, pos.Y))
- { moveto(pos = loc(pos.X - 1, pos.Y)); i = 0; }
- else break;
- }
- local = 1;
- return i;
- }
- ////////////////////////
- rect get_line() // return the line under current
- { // pixel
- loc pos = getCP();
- rect line_below = rect(pos, pos);
- for(int i = 0; i <= used; i++)
- {
- if(level[i].corner.Y == pos.Y && level[i].contains(pos))
- {
- line_below = level[i];
- break;
- }
- }
- return line_below;
- }
- ///////////////////////
- void remove(rect r) // remove line from array
- {
- for(int i = 0; i <= used; i++)
- {
- if(level[i].origin.Y == r.origin.Y
- && level[i].origin.X == r.origin.X)
- {
- for(; i <= used; i++)
- level[i] = level[i + 1];
- used--;
- break;
- }
- }
- }
- ///////////////////////
- void add(rect r) // add line to array. Lines are sorted
- { // by Y, then by X
- int i;
- for(i = 0; i <= used; i++)
- {
- if(level[i].origin.Y == r.origin.Y && level[i].origin.X > r.origin.X)
- break;
- else if(level[i].origin.Y > r.origin.Y)
- break;
- }
- for(int j = used; j >= i; j--)
- level[j + 1] = level[j];
- if(i > used)
- i--;
- level[i] = r;
- work = r;
- used++;
- }
- ///////////////////////////
- void draw_line() // draws line with current colors and pattern
- {
- flag = 1;
- loc pos = getCP();
- rect line_below = get_line(); // if we draw line above any other
- rect new_rect; // contains new line
- remove(line_below);
- loc p = getCP();
- if(!local) // it is not local minimum
- {
- moveto(loc(line_below.origin.X, line_below.origin.Y - 1)); // go up
- while(1)
- {
- p = getCP();
- if((!get_pixel(p)) // no place above leftmost pixel
- && line_below.corner.X >= p.X) // and we can move right
- {
- if(line_below.corner.X == p.X) // if it is the end of
- { // line_below
- if(!used) // if no ways from this
- flag = 0; // point
- break;
- }
- else moveto(loc(p.X + 1, p.Y));
- }
- else break;
- }
-
- while(1)
- {
- p = getCP();
- if((get_pixel(p.X - 1, p.Y)) // left, but not dn
- && get_pixel(p)
- && (!get_pixel(p.X, p.Y + 1)
- || p.X >= line_below.origin.X) // or we are above
- && !inside(loc(p.X - 1, p.Y - 1))) // and no intersection
- moveto(p.X - 1, p.Y); // with other lines
- else
- {
- if(line_below.origin.X > p.X // we can go_local
- && get_pixel(p.X, p.Y + 1))
- flag = 0;
- break;
- }
- }
- }
- int mark = 0;
- if(get_pixel(p)) // first pixel of line
- {
- putPixel(p);
- mark = 1; // mark that at least one pixel drawn
- }
- new_rect = rect(p, p);
-
- int disable = 1; // first time impossible to put pixel
-
- while(1)
- {
- p = loc(p.X + 1, p.Y);
- if( mark
- && get_pixel(p)
- && (p.X <= line_below.corner.X
- || (!get_pixel(p.X, p.Y + 1)
- || inside(p))))
- {
- putPixel(p); // draw line in cycle
- if(disable)
- new_rect.corner = p;
- else
- new_rect.origin = p;
- disable = 1;
- }
- else
- {
- if(disable && mark) // all with this line
- add(new_rect);
- if(get_pixel(p.X, p.Y + 1) // we can go_local
- && get_pixel(p))
- flag = 0;
- if(p.X >= line_below.corner.X)
- break;
- else
- new_rect = rect(p, loc(line_below.corner.X,
- line_below.corner.Y - 1));
- disable = 0;
- }
- moveto(p);
- }
- local = 0;
- }
- ////////////////////////
- void dump_levels() // simplify the level[]
- {
- for(int i = 0; i < used; i++)
- for(int j = i + 1; j < used; j++)
- {
- if(level[i].origin.X - 1 == level[j].corner.X
- && level[i].origin.Y == level[j].origin.Y)
- {
- level[i].origin.X = level[j].origin.X;
- remove(level[j]);
- }
- if(level[i].corner.X + 1 == level[j].origin.X
- && level[i].origin.Y == level[j].origin.Y)
- {
- level[i].corner.X = level[j].corner.X;
- remove(level[j]);
- }
-
- if(level[i].corner.X >= level[j].origin.X
- && level[i].origin.X <= level[j].corner.X
- && level[i].origin.Y == level[j].origin.Y - 1)
- {
- if(level[i].origin.X <= level[j].origin.X)
- level[j].origin.X = level[i].corner.X;
- else
- level[j].corner.X = level[i].origin.X;
- if(level[j].origin.X >= level[j].corner.X)
- remove(level[j]);
- }
- }
- }
- ////////////////////////
- void fill_cell() // fill before it is possible to go_local
- {
- while(flag && used && !kbhit())
- {
- loc pos = getCP();
-
- if(pos.Y == 75)
- pos.Y = 75;
-
- moveto(level[used - 1].corner);
- draw_line();
- dump_levels();
- }
- if(kbhit())
- for(int i = 0; i < 640; i++)
- level[i] = rect(0, 0, 0, 0);
- }
- ////////////////////////
- void fill_area(loc from, int a, int b, char* f,
- rect r = rect(0, 0, getmaxx(), getmaxy()))
- {
- memset(level, 0, 640 * sizeof(rect));
- if(!r.contains(from))
- return;
- flag = 1;
- moveto(from);
- start_color = getpixel(from);
- attr = a; bak = b; bound = r; fill = f;
-
- go_local(); // go to start position
- draw_line(); // draw the first line and add first line to level[]
-
- while(1)
- {
- if(flag)
- fill_cell();
- flag = 1;
- if(!used)
- return;
- moveto(work.origin);
- if(go_local()) // left local min
- {
- moveto(work.corner.X + 1, work.corner.Y);
- if(go_local(0)) // right local min
- return;
- }
- draw_line();
- }
- }
- ////////////////////////
- /*
- void main()
- {
- int gdriver = DETECT, gmode;
- initgraph(&gdriver, &gmode, "..\\BGI");
-
- flag = 0;
- used = 0;
-
- circle(120, 120, 100);
- line(80, 80, 100, 100);
- line(100, 100, 120, 100);
- line(120, 100, 140, 75);
-
- loc from(125, 125);
- // char fill[] = { 255, 255, 255, 255, 255, 255, 255, 254 };
- char fill_pattern[] = { 255, 55, 25, 5, 1, 215, 155, 15 };
- int attr = GREEN;
- int bak = BLACK;
- fill_area(from, attr, bak, fill_pattern); //, r);
- }
- */